#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>

#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

// libxml2
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

#include <time.h>
#include <pthread.h>

#include "bsd_queue.h"

#define wsdd_port 3702
// 1024以下要root权限，用8080端口算了
#define http_port 8080
#define rtsp_port 8554

#define recv_buf_size 10 * 1024

#define xml_repaeat_find_device "ipc_repeat_find_device.xml"
#define xml_GetSystemDateAndTime_respon "GetSystemDateAndTime_respon.xml"
#define xml_GetCapabilities_respon "GetCapabilities_respon.xml"
#define xml_GetProfiles_respon "GetProfiles_respon.xml"
#define xml_GetDeviceInformation_respon "GetDeviceInformation_response.xml"
#define xml_GetStreamUri_respon "GetStreamUri_respon.xml"

#define MAX_CONN_ACCEPT 20
#define MAX_CONN_TIMEOUT 30

/* A multi-family sockaddr. */
typedef union
{
    struct sockaddr sa;
    struct sockaddr_in sa_in;
} usockaddr;

typedef struct conn_item
{
    TAILQ_ENTRY(conn_item)
    entry;
    int fd;
    usockaddr usa;
} conn_item_t;

typedef struct conn_list
{
    TAILQ_HEAD(, conn_item)
    head;
    int count;
} conn_list_t;

static int daemon_exit = 0;
struct in_addr local_ip;

// probe子线程的tid
pthread_t probe_tid = 0;

int save_log(char *log_content)
{
    //
}

/**
 * print_element_names:
 * @a_node: the initial xml node to consider.
 *
 * Prints the names of the all the xml elements
 * that are siblings or children of a given xml node.
 */
static void
print_element_names(xmlNode *a_node)
{
    xmlNode *cur_node = NULL;

    for (cur_node = a_node; cur_node; cur_node = cur_node->next)
    {
        if (cur_node->type == XML_ELEMENT_NODE)
        {
            printf("node type: Element, name: %s\n", cur_node->name);
        }

        print_element_names(cur_node->children);
    }
}

unsigned int get_route_if_ip(int socket_fd, unsigned int dst_ip)
{

    struct sockaddr_in *sin;
    struct ifreq *ifr;
    struct ifconf conf;
    char buff[BUFSIZ];
    int num;

    socket_fd = socket(AF_INET, SOCK_DGRAM, 0);

    conf.ifc_len = BUFSIZ;
    conf.ifc_buf = buff;

    ioctl(socket_fd, SIOCGIFCONF, &conf);

    num = conf.ifc_len / sizeof(struct ifreq);
    ifr = conf.ifc_req;

    for (int i = 0; i < num; i++)
    {
        struct sockaddr_in *sin = (struct sockaddr_in *)(&ifr->ifr_addr);

        ioctl(socket_fd, SIOCGIFFLAGS, ifr);
        if (((ifr->ifr_flags & IFF_LOOPBACK) == 0) && (ifr->ifr_flags & IFF_UP))
        {
            return sin->sin_addr.s_addr;
        }

        ifr++;
    }

    return 0;
}

// 根据名字找子节点
xmlNode *find_child(xmlNode *par_node, char *name)
{
    xmlNode *cur_node = NULL;
    for (cur_node = par_node->children; cur_node; cur_node = cur_node->next)
    {
        if (xmlStrcmp(cur_node->name, BAD_CAST name) == 0)
        {
            return cur_node;
        }
    }
    printf("***no node %s", name);
    return cur_node;
}

void *probe_thread(void *argv)
{
    int ret = -1;
    int len = 0;
    int on = 1; // 端口重用
    char ipbuf[100];
    struct sockaddr_in wsdd_addr_serv;

    printf("wsdd port is %d\n", wsdd_port);

    /* wsdd_sock_fd --- wsdd socket文件描述符 创建udp套接字*/
    int wsdd_sock_fd = socket(AF_INET, SOCK_DGRAM, 0);

    if (wsdd_sock_fd < 0)
    {
        perror("wsdd_socket");
        exit(1);
    }

    ret = setsockopt(wsdd_sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    // 找本机ip
    local_ip.s_addr = get_route_if_ip(wsdd_sock_fd, 0);
    if (local_ip.s_addr != 0)
    {
        sprintf(ipbuf, "http://%s:8080/onvif/device_service", inet_ntoa(local_ip));
        printf("Xaddrs: %s\n", ipbuf);
    }

    /* 将套接字和IP、端口绑定 */

    memset(&wsdd_addr_serv, 0, sizeof(struct sockaddr_in)); // 每个字节都用0填充
    wsdd_addr_serv.sin_family = AF_INET;                    // 使用IPV4地址
    wsdd_addr_serv.sin_port = htons(wsdd_port);             // 端口
    /* INADDR_ANY表示不管是哪个网卡接收到数据，只要目的端口是SERV_PORT，就会被该应用程序接收到 */
    wsdd_addr_serv.sin_addr.s_addr = htonl(INADDR_ANY); // 自动获取IP地址
    len = sizeof(wsdd_addr_serv);

    /* 绑定socket */
    if (bind(wsdd_sock_fd, (struct sockaddr *)&wsdd_addr_serv, len) < 0)
    {
        perror("wsdd_port bind error:");
        exit(1);
    }

    // 加入组播
    struct ip_mreq mreq;                                      /*加入广播组*/
    mreq.imr_multiaddr.s_addr = inet_addr("239.255.255.250"); /*广播地址*/
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);            /*网络接口为默认*/

    ret = setsockopt(wsdd_sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

    int recv_num;
    int send_num;
    char send_buf[4 * 1024];
    char recv_buf[4 * 1024];
    char message_id[100];

    struct sockaddr_in addr_client;
    // memset(send_buf, 'x', sizeof(send_buf));
    // while (1)
    //{
    printf("server wait:\n");

    recv_num = recvfrom(wsdd_sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_client, (socklen_t *)&len);

    if (recv_num < 0)
    {
        perror("recvfrom error:");
        exit(1);
    }

    recv_buf[recv_num] = '\0';
    printf("server receive %d bytes: %s\n", recv_num, recv_buf);

    xmlDoc *doc = NULL;

    // struct _xmlNode {
    // void *	_private	: application data
    // xmlElementType	type	: type number, must be second !
    // const xmlChar *	name	: the name of the node, or the entity
    // struct _xmlNode *	children	: parent->childs link
    // struct _xmlNode *	last	: last child link
    // struct _xmlNode *	parent	: child->parent link
    // struct _xmlNode *	next	: next sibling link
    // struct _xmlNode *	prev	: previous sibling link
    // struct _xmlDoc *	doc	: the containing document End of common part
    // xmlNs *	ns	: pointer to the associated namespace
    // xmlChar *	content	: the content
    // struct _xmlAttr *	properties	: properties list
    // xmlNs *	nsDef	: namespace definitions on this node
    // void *	psvi	: for type/PSVI information
    // unsigned short	line	: line number
    // unsigned short	extra	: extra data for XPath/XSLT
    //} xmlNode;

    xmlNode *root_element = NULL;
    xmlXPathContextPtr xpathCtx = NULL;

    xmlNode *cur_node = NULL;
    xmlNode *body_child_node = NULL;

    /*
     * this initialize the library and check potential ABI mismatches
     * between the version it was compiled for and the actual shared
     * library used.
     */
    LIBXML_TEST_VERSION

    // parse the file and get the DOM
    doc = xmlParseMemory(recv_buf, recv_num);
    if (doc == NULL)
    {
        printf("error: could not parse recv_buf\n");
    }

    /*Get the root element node */
    root_element = xmlDocGetRootElement(doc);

    /* Create xpath evaluation context */
    xpathCtx = xmlXPathNewContext(doc);

    // print_element_names(root_element, xpathCtx);

    // 判断root_element的name是否为Envelope
    if (xmlStrcmp(root_element->name, BAD_CAST "Envelope") == 0)
    {
        printf("root element is Envelope \n");
        // 历遍Envelope的子node，看看有没有Body这个
        for (cur_node = root_element->children; cur_node; cur_node = cur_node->next)
        {
            if (xmlStrcmp(cur_node->name, BAD_CAST "Body") == 0)
            {
                printf("element is Body \n");
                // 看看body里面有设么node
                for (body_child_node = cur_node->children; body_child_node; body_child_node = body_child_node->next)
                {
                    printf("child element is %s \n", body_child_node->name);

                    // Probe 项的处理
                    if (xmlStrcmp(body_child_node->name, BAD_CAST "Probe") == 0)
                    {
                        xmlNode *probe_node = NULL;
                        for (probe_node = body_child_node->children; probe_node; probe_node = probe_node->next)
                        {
                            printf("probe child element is %s \n", probe_node->name);
                            // 如果name是 Types， 显示type的类型
                            if (xmlStrcmp(probe_node->name, BAD_CAST "Types") == 0)
                            {
                                xmlChar *typeCont = NULL;

                                typeCont = xmlNodeGetContent(probe_node);

                                printf("probe types is %s \n", typeCont);
                                // 去掉:前的命名空间
                                typeCont = (xmlChar *)xmlStrchr(typeCont, ':');
                                typeCont++;
                                if (xmlStrcmp(typeCont, BAD_CAST "NetworkVideoTransmitter") == 0 || xmlStrcmp(typeCont, BAD_CAST "Device") == 0)
                                {
                                    printf("reply probe NetworkVideoTransmitter \n");
                                    // 先获得MessageID
                                    xmlNode *header = NULL;
                                    xmlNode *ms_id = NULL;
                                    for (header = root_element->children; header; header = header->next)
                                    {
                                        if (xmlStrcmp(header->name, BAD_CAST "Header") == 0)
                                        {
                                            for (ms_id = header->children; ms_id; ms_id = ms_id->next)
                                            {
                                                // messageid 项的处理
                                                if (xmlStrcmp(ms_id->name, BAD_CAST "MessageID") == 0)
                                                {
                                                    // 把找设备xml的messageid放到message_id里面
                                                    // 回复时候插入到 Header/RelatesTo项里面
                                                    sprintf(message_id, "%s", xmlNodeGetContent(ms_id));

                                                    // 生成回复xml的messgageid
                                                    char uuid[100];
                                                    srand(time(NULL));
                                                    sprintf(uuid, "urn:uuid:%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
                                                            rand() >> 16, rand() >> 16, rand() >> 16, rand() >> 16, rand() >> 16, rand() >> 16, rand() >> 16, rand() >> 16);

                                                    // 读取xml文件，更改其中的内容
                                                    xmlDocPtr doc_rpl; /* the resulting document tree */
                                                    xmlNode *root_element_find_device = NULL;
                                                    xmlNode *child_node = NULL;

                                                    doc_rpl = xmlReadFile(xml_repaeat_find_device, NULL, 0);
                                                    if (doc_rpl == NULL)
                                                    {
                                                        fprintf(stderr, "Failed to parse %s\n", xml_repaeat_find_device);
                                                    }
                                                    /*Get the root element node */
                                                    // Body/ProbeMatches/ProbeMatch/EndpointReference/Address 更改
                                                    // Body/ProbeMatches/ProbeMatch/XAddr 更改
                                                    root_element_find_device = xmlDocGetRootElement(doc_rpl);
                                                    child_node = find_child(root_element_find_device, "Body");
                                                    if (child_node)
                                                    {
                                                        child_node = find_child(child_node, "ProbeMatches");
                                                        if (child_node)
                                                        {
                                                            child_node = find_child(child_node, "ProbeMatch");
                                                            if (child_node)
                                                            {
                                                                xmlNode *child_node_cur = NULL;

                                                                // 找Body/ProbeMatches/ProbeMatch/XAddr并更改
                                                                child_node_cur = find_child(child_node, "XAddrs");
                                                                if (child_node_cur)
                                                                {
                                                                    xmlNodeSetContent(child_node_cur, BAD_CAST ipbuf);
                                                                }

                                                                // 找Body/ProbeMatches/ProbeMatch/EndpointReference/Address并更改
                                                                child_node_cur = find_child(child_node, "EndpointReference");
                                                                if (child_node_cur)
                                                                {
                                                                    child_node_cur = find_child(child_node_cur, "Address");
                                                                    if (child_node_cur)
                                                                    {
                                                                        xmlNodeSetContent(child_node_cur, BAD_CAST uuid);
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }

                                                    // 找Header/RelatesTo
                                                    // 找Header/MessageID

                                                    child_node = find_child(root_element_find_device, "Header");
                                                    if (child_node)
                                                    {
                                                        xmlNode *child_node_cur = NULL;
                                                        // 找Header/RelatesTo 并更改
                                                        child_node_cur = find_child(child_node, "RelatesTo");
                                                        if (child_node_cur)
                                                        {
                                                            xmlNodeSetContent(child_node_cur, BAD_CAST message_id);
                                                        }

                                                        // 找Header/MessageID 并更改
                                                        child_node_cur = find_child(child_node, "MessageID");
                                                        if (child_node_cur)
                                                        {
                                                            xmlNodeSetContent(child_node_cur, BAD_CAST uuid);
                                                        }
                                                    }

                                                    xmlChar *xmlbuff;
                                                    int buffersize;

                                                    xmlDocDumpFormatMemory(doc_rpl, &xmlbuff, &buffersize, 1);
                                                    printf("%s", (char *)xmlbuff);
                                                    printf("\n");
                                                    printf("%s\n", inet_ntoa(addr_client.sin_addr));
                                                    int rlen = sendto(wsdd_sock_fd, xmlbuff, buffersize, 0, (struct sockaddr *)&addr_client, sizeof(struct sockaddr_in));

                                                    xmlFreeDoc(doc_rpl);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else if (xmlStrcmp(cur_node->name, BAD_CAST "Header") == 0)
            {
                printf("element is Header \n");
            }
        }
    }

    /*free the document */
    xmlXPathFreeContext(xpathCtx);
    xmlFreeDoc(doc);
    xmlCleanupParser();
    //}

    /*
     *Free the global variables that may
     *have been allocated by the parser.
     */

    close(wsdd_sock_fd);

    printf("probe_thread exit.\r\n");

    probe_tid = 0;

    return NULL;
}
int onvif_respon(char *psend_buf, const conn_item_t *item, xmlChar *xml_send_buff, int xml_send_buff_size)
{
    int send_size = -1;
    memset(psend_buf, 0, recv_buf_size);

    send_size = sprintf(psend_buf, "HTTP/1.1 200 OK\r\n"
                                   "Server: hsoap/2.8\r\n"
                                   "Content-Type: %s\r\n"
                                   "Content-Length: %d\r\n"
                                   "Connection: close\r\n\r\n",
                        "application/soap+xml; charset=utf-8", xml_send_buff_size);
    memcpy(psend_buf + send_size, xml_send_buff, xml_send_buff_size);
    send_size = send_size + xml_send_buff_size;
    printf("%s", psend_buf);
    printf("\n");
    int rlen = sendto(item->fd, psend_buf, send_size, 0, (struct sockaddr *)&(item->usa.sa_in), sizeof(struct sockaddr_in));
    printf("send to: %s:%d\n", inet_ntoa(item->usa.sa_in.sin_addr), ntohs(item->usa.sa_in.sin_port));
    printf("%d should be send and %d have been send.\n", send_size, rlen);
    if (send_size == rlen)
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

static xmlNode *
find_node_by_name(xmlNode *a_node, char *name)
{
    xmlNode *cur_node = NULL;
    xmlNode *chil_node = NULL;

    for (cur_node = a_node; cur_node; cur_node = cur_node->next)
    {
        if (cur_node->type == XML_ELEMENT_NODE)
        {
            // printf("node type: Element, name: %s\n", cur_node->name);
            if (xmlStrcmp(cur_node->name, BAD_CAST name) == 0)
            {
                return cur_node;
            }
        }

        chil_node = find_node_by_name(cur_node->children, BAD_CAST name);
        if (chil_node != NULL)
        {
            return chil_node;
        }
    }
    return NULL;
}

typedef struct sp
{
    int sp;
    char rec_tmp[4 * 1024];
    int file_len;
    int rec_len;
} struct_sp;

struct_sp sp_info;

static void
handle_request(FILE *conn_fp, const conn_item_t *item)
{
    char line[4096];
    // Parse the first line of the request.
    // if (!fgets(line, sizeof(line), conn_fp)) {
    //	printf("Bad Request\n");
    //	return;
    //}

    struct sockaddr_in addr_client;
    int recv_num = -1;
    char recv_buf[recv_buf_size];
    char print_buf[1024];
    char xmlbuff[4 * 1024];
    char *xmlprt = xmlbuff;
    int xml_len = 0;
    int len = sizeof(addr_client);
    int print_len = 0;
    char *pfile_len;
    char *pfile_len_end;
    char str_file_len[20];

    FILE *logfile = NULL;

    memset(recv_buf, 0, sizeof(recv_buf));
    memset(print_buf, 0, sizeof(print_buf));
    memset(str_file_len, 0, sizeof(str_file_len));
    recv_num = recvfrom(item->fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_client, &len);

    printf("****************************\n");
    // printf("recv_num is: %d, from :%s :%d, len is: %d\n", recv_num, inet_ntoa(addr_client.sin_addr), ntohs(addr_client.sin_port), len);
    printf("recv_num is: %d\n", recv_num);
    printf("%s", recv_buf);
    printf("\n");
    printf("****************************\n");

    logfile = fopen("./logfile.txt", "ab+");

    print_len = sprintf(print_buf, "****************************\n");
    fwrite(print_buf, 1, print_len, logfile);

    print_len = sprintf(print_buf, "recv_num is: %d, len is: %d\n", recv_num, len);
    fwrite(print_buf, 1, print_len, logfile);

    fwrite(recv_buf, 1, recv_num, logfile);

    print_len = sprintf(print_buf, "\n");
    fwrite(print_buf, 1, print_len, logfile);

    print_len = sprintf(print_buf, "****************************\n");
    fwrite(print_buf, 1, print_len, logfile);

    fflush(logfile);
    fclose(logfile);

    // 获取文件长度

    // 判断一下看看是不是分包后的数据
    if (sp_info.sp)
    {
        // 分包后的数据，直接把数据拷到 tmp里面
        memcpy(sp_info.rec_tmp + sp_info.rec_len, recv_buf, recv_num);
        // 接收数增加
        sp_info.rec_len += recv_num;
        // 判断是否完全传输了
        if (sp_info.rec_len > sp_info.file_len)
        {
            // 传输错误，清除数据返回。
            memset(&sp_info, 0, sizeof(struct_sp));
            printf("**********xml transt ***ERR***! \n");
            return;
        }
        else if (sp_info.rec_len < sp_info.file_len)
        {
            printf("**********xml transt ***NOT*** finish! \n");
            return;
        }
        else
        {
            // 传输完成
            memcpy(xmlprt, sp_info.rec_tmp, sp_info.file_len);
            xml_len = sp_info.file_len;
            memset(&sp_info, 0, sizeof(struct_sp));
        }
    }
    else
    {
        // 第一个包，可以取Content-Length

        // 看看接收的数据里面是否有文件长度
        pfile_len = strstr(recv_buf, "Content-Length");
        //
        if (pfile_len)
        {
            // 有长度，去除 ：
            pfile_len = strstr(pfile_len, ":");
            if (pfile_len)
            {
                // 找到:，跳过，去除空格
                pfile_len++;
                while (*pfile_len == ' ')
                {
                    //
                    pfile_len++;
                }

                pfile_len_end = pfile_len;
                while (*pfile_len_end != ' ' && *pfile_len_end != '\r' && *pfile_len_end != '\n')
                {
                    pfile_len_end++;
                }

                memcpy(&str_file_len, pfile_len, pfile_len_end - pfile_len);

                // 转换字符串到int。
                sp_info.file_len = atoi(str_file_len);
                printf("***************Content-Length is: %d \n", sp_info.file_len);

                // int xml_len = recv_num;
                xml_len = recv_num;
                for (; xml_len > 1; xml_len--)
                {
                    if (recv_buf[recv_num - xml_len] == '<')
                    {
                        if (recv_buf[recv_num - xml_len + 1] == '?')
                        {
                            // xml开始的位置和长度
                            // xmlprt = recv_buf + recv_num - xml_len;
                            printf("**************xml file len is: %d \n", xml_len);
                            // memcpy(xmlprt, recv_buf + recv_num - xml_len, xml_len);

                            if (xml_len < sp_info.file_len)
                            {
                                // 未传输完成
                                // 那边关了链接是无法再接收余下部分
                                sp_info.sp = 0;
                                sp_info.rec_len = xml_len;
                                memcpy(sp_info.rec_tmp, recv_buf + recv_num - xml_len, xml_len);

                                printf("**********xml transt ***NOT*** finish! \n");
                                return;
                            }
                            else if (xml_len > sp_info.file_len)
                            {
                                // 传输错误，清除数据返回。
                                memset(&sp_info, 0, sizeof(struct_sp));
                                printf("**********xml transt ***ERR***! \n");
                                return;
                            }
                            else
                            {
                                // 一包传完无分包
                                memset(&sp_info, 0, sizeof(struct_sp));
                                printf("**********xml transt finish! \n");
                                // 拷到xml文件缓存里面
                                memcpy(xmlprt, recv_buf + recv_num - xml_len, xml_len);
                                break;
                            }
                        }
                    }
                }
            }
            else
            {
                // 传输错误，清除数据返回。
                memset(&sp_info, 0, sizeof(struct_sp));
                printf("**********xml transt ***ERR***! \n");
                return;
            }
        }
        else
        {
            // 传输错误，清除数据返回。
            memset(&sp_info, 0, sizeof(struct_sp));
            printf("**********xml transt ***ERR***! \n");
            return;
        }
    }

    // 处理xml文件

    xmlDoc *doc = NULL;
    xmlNode *root_element = NULL;
    xmlXPathContextPtr xpathCtx = NULL;

    xmlNode *cur_node = NULL;
    xmlNode *body_child_node = NULL;

    // parse the file and get the DOM
    doc = xmlParseMemory(xmlprt, xml_len);
    if (doc == NULL)
    {
        printf("error: could not parse recv_buf\n");
    }

    /*Get the root element node */
    root_element = xmlDocGetRootElement(doc);
    print_element_names(root_element);

    // cur_node = find_node_by_name(root_element, "GetCapabilities");
    // if (cur_node != NULL)
    //{
    //     printf("************find the GetCapabilities node!\n");

    // Category
    //    cur_node = find_node_by_name(root_element, "Category");
    //    if (cur_node != NULL)
    //    {
    //        printf("************find the Category node!\n");
    //        printf("************Category node cnontex is %s!\n", xmlNodeGetContent(cur_node));
    //    }
    //}

    // 判断root_element的name是否为Envelope
    if (xmlStrcmp(root_element->name, BAD_CAST "Envelope") == 0)
    {
        // 是的话历遍子节点
        for (cur_node = root_element->children; cur_node; cur_node = cur_node->next)
        {
            // 如果是Header
            if (xmlStrcmp(cur_node->name, BAD_CAST "Header") == 0)
            {
                // Header的处理程序
            }
            // body
            else if (xmlStrcmp(cur_node->name, BAD_CAST "Body") == 0)
            {
                // Body的处理程序
                xmlNode *body_child = NULL;
                // 历遍body的子节点
                for (body_child = cur_node->children; body_child; body_child = body_child->next)
                {
                    // GetDeviceInformation
                    if (xmlStrcmp(body_child->name, BAD_CAST "GetDeviceInformation") == 0)
                    {
                        printf("****GetDeviceInformation is %s\n", xmlNodeGetContent(body_child));

                        xmlDocPtr doc_rsp_dev; /* the resulting document tree */

                        doc_rsp_dev = xmlReadFile(xml_GetDeviceInformation_respon, NULL, 0);
                        if (doc_rsp_dev == NULL)
                        {
                            fprintf(stderr, "Failed to parse %s\n", xml_GetDeviceInformation_respon);
                        }

                        xmlChar *xml_send_buff;
                        int xml_send_buff_size;

                        xmlDocDumpFormatMemory(doc_rsp_dev, &xml_send_buff, &xml_send_buff_size, 1);
                        onvif_respon(recv_buf, item, xml_send_buff, xml_send_buff_size);

                        xmlFreeDoc(doc_rsp_dev);
                    }
                    // GetProfiles
                    else if (xmlStrcmp(body_child->name, BAD_CAST "GetProfiles") == 0)
                    {
                        printf("****GetProfiles is %s\n", xmlNodeGetContent(body_child));
                        // MainStream
                        //   找GetProfilesResponse/Profiles/VideoEncoderConfiguration/Multicast/Address/IPv4Address
                        //   找GetProfilesResponse/Profiles/AudioEncoderConfiguration/Multicast/Address/IPv4Address

                        // SubStream
                        //   找GetProfilesResponse/Profiles/VideoEncoderConfiguration/Multicast/Address/IPv4Address
                        //   找GetProfilesResponse/Profiles/AudioEncoderConfiguration/Multicast/Address/IPv4Address

                        // 读取xml文件，更改其中的内容
                        xmlDocPtr doc_rsp_pro; /* the resulting document tree */
                        xmlNode *root_element_rsp_pro = NULL;
                        xmlNode *pro_node = NULL;

                        doc_rsp_pro = xmlReadFile(xml_GetProfiles_respon, NULL, 0);
                        if (doc_rsp_pro == NULL)
                        {
                            fprintf(stderr, "Failed to parse %s\n", xml_GetProfiles_respon);
                        }
                        root_element_rsp_pro = xmlDocGetRootElement(doc_rsp_pro);
                        if (root_element_rsp_pro == NULL)
                        {
                            return;
                        }

                        // 找到GetProfilesResponse 节点
                        pro_node = find_node_by_name(root_element_rsp_pro, "GetProfilesResponse");
                        // 找到里面的第一个Profiles节点
                        xmlNode *profile_node = NULL;
                        for (profile_node = pro_node->children; profile_node; profile_node = profile_node->next)
                        {
                            if (xmlStrcmp(profile_node->name, BAD_CAST "Profiles") == 0)
                            {
                                char pro_buff[100];
                                memset(&pro_buff, 0, sizeof(pro_buff));
                                sprintf(pro_buff, "%s", inet_ntoa(local_ip));

                                xmlNode *ipaddr_node = find_node_by_name(find_node_by_name(profile_node, "VideoEncoderConfiguration"), "IPv4Address");
                                xmlNodeSetContent(ipaddr_node, BAD_CAST pro_buff);

                                ipaddr_node = find_node_by_name(find_node_by_name(profile_node, "AudioEncoderConfiguration"), "IPv4Address");
                                xmlNodeSetContent(ipaddr_node, BAD_CAST pro_buff);
                            }
                        }

                        xmlChar *xml_send_buff;
                        int xml_send_buff_size;

                        xmlDocDumpFormatMemory(doc_rsp_pro, &xml_send_buff, &xml_send_buff_size, 1);
                        onvif_respon(recv_buf, item, xml_send_buff, xml_send_buff_size);

                        xmlFreeDoc(doc_rsp_pro);
                    }
                    // GetCapabilities
                    else if (xmlStrcmp(body_child->name, BAD_CAST "GetCapabilities") == 0)
                    {
                        printf("****GetCapabilities is %s\n", xmlNodeGetContent(body_child));

                        // 读取xml文件，更改其中的内容
                        xmlDocPtr doc_rsp_cap; /* the resulting document tree */
                        xmlNode *root_element_rsp_cap = NULL;
                        xmlNode *cap_node = NULL;

                        doc_rsp_cap = xmlReadFile(xml_GetCapabilities_respon, NULL, 0);
                        if (doc_rsp_cap == NULL)
                        {
                            fprintf(stderr, "Failed to parse %s\n", xml_GetCapabilities_respon);
                        }
                        root_element_rsp_cap = xmlDocGetRootElement(doc_rsp_cap);
                        if (root_element_rsp_cap == NULL)
                        {
                            return;
                        }
                        char cap_buff[100];
                        // 更改Analytics/XAddr
                        cap_node = find_node_by_name(find_node_by_name(root_element_rsp_cap, "Analytics"), "XAddr");
                        sprintf(cap_buff, "http://%s:8080/onvif/Analyticse", inet_ntoa(local_ip));
                        xmlNodeSetContent(cap_node, BAD_CAST cap_buff);
                        // 更改Device/XAddr
                        cap_node = find_node_by_name(find_node_by_name(root_element_rsp_cap, "Device"), "XAddr");
                        sprintf(cap_buff, "http://%s:8080/onvif/device_service", inet_ntoa(local_ip));
                        xmlNodeSetContent(cap_node, BAD_CAST cap_buff);
                        // 更改Events/XAddr
                        cap_node = find_node_by_name(find_node_by_name(root_element_rsp_cap, "Events"), "XAddr");
                        sprintf(cap_buff, "http://%s:8080/onvif/Events", inet_ntoa(local_ip));
                        xmlNodeSetContent(cap_node, BAD_CAST cap_buff);
                        // 更改Imaging/XAddr
                        cap_node = find_node_by_name(find_node_by_name(root_element_rsp_cap, "Imaging"), "XAddr");
                        sprintf(cap_buff, "http://%s:8080/onvif/Imaging", inet_ntoa(local_ip));
                        xmlNodeSetContent(cap_node, BAD_CAST cap_buff);
                        // 更改Media/XAddr
                        cap_node = find_node_by_name(find_node_by_name(root_element_rsp_cap, "Media"), "XAddr");
                        sprintf(cap_buff, "http://%s:8080/onvif/Media", inet_ntoa(local_ip));
                        xmlNodeSetContent(cap_node, BAD_CAST cap_buff);
                        // 更改PTZ/XAddr
                        cap_node = find_node_by_name(find_node_by_name(root_element_rsp_cap, "Ptz"), "XAddr");
                        sprintf(cap_buff, "http://%s:8080/onvif/Ptz", inet_ntoa(local_ip));
                        xmlNodeSetContent(cap_node, BAD_CAST cap_buff);

                        xmlChar *xml_send_buff;
                        int xml_send_buff_size;

                        xmlDocDumpFormatMemory(doc_rsp_cap, &xml_send_buff, &xml_send_buff_size, 1);
                        // printf("%s", (char *)xml_send_buff);
                        // printf("\n");
                        // printf("%s:%d\n", inet_ntoa(item->usa.sa_in.sin_addr), ntohs(item->usa.sa_in.sin_port));
                        // int rlen = sendto(item->fd, xml_send_buff, xml_send_buff_size, 0, (struct sockaddr *)&(item->usa.sa_in), sizeof(struct sockaddr_in));

                        // 写http响应头, 反正 recv_buf 和 recv_num = 现在已经没有用处了，直接使用;
                        // recv_num = -1;
                        // memset(recv_buf, 0, sizeof(recv_buf));

                        // recv_num = sprintf(recv_buf, "HTTP/1.1 200 OK\r\n"
                        //                              "Server: hsoap/2.8\r\n"
                        //                              "Content-Type: %s\r\n"
                        //                              "Content-Length: %d\r\n"
                        //                              "Connection: close\r\n\r\n",
                        //                    "application/soap+xml; charset=utf-8", xml_send_buff_size);
                        // memcpy(recv_buf + recv_num, xml_send_buff, xml_send_buff_size);
                        // recv_num = recv_num + xml_send_buff_size;
                        // printf("%s", recv_buf);
                        // printf("\n");
                        // int rlen = sendto(item->fd, recv_buf, recv_num, 0, (struct sockaddr *)&(item->usa.sa_in), sizeof(struct sockaddr_in));
                        // printf("send to: %s:%d\n", inet_ntoa(item->usa.sa_in.sin_addr), ntohs(item->usa.sa_in.sin_port));
                        // printf("%d should be send and %d have been send.\n", recv_num, rlen);
                        onvif_respon(recv_buf, item, xml_send_buff, xml_send_buff_size);

                        xmlFreeDoc(doc_rsp_cap);
                    }
                    // GetSystemDateAndTime
                    else if (xmlStrcmp(body_child->name, BAD_CAST "GetSystemDateAndTime") == 0)
                    {
                        printf("****GetSystemDateAndTime is %s\n", xmlNodeGetContent(body_child));

                        // 获得当前时间
                        time_t time_utc;
                        struct tm tm_local;
                        struct tm tm_utc;
                        char utc_hour[3];
                        char utc_min[3];
                        char utc_sec[3];
                        char utc_year[5];
                        char utc_mon[3];
                        char utc_day[3];

                        char loc_hour[3];
                        char loc_min[3];
                        char loc_sec[3];
                        char loc_year[5];
                        char loc_mon[3];
                        char loc_day[3];

                        // Get the UTC time
                        time(&time_utc);

                        // Get the local time
                        // Use localtime_r for threads safe
                        localtime_r(&time_utc, &tm_local);
                        // utc tm
                        gmtime_r(&time_utc, &tm_utc);

                        sprintf(utc_hour, "%d", tm_utc.tm_hour);
                        sprintf(utc_min, "%d", tm_utc.tm_min);
                        sprintf(utc_sec, "%d", tm_utc.tm_sec);
                        sprintf(utc_year, "%d", tm_utc.tm_year);
                        sprintf(utc_mon, "%d", tm_utc.tm_mon);
                        sprintf(utc_day, "%d", tm_utc.tm_mday);

                        sprintf(loc_hour, "%d", tm_local.tm_hour);
                        sprintf(loc_min, "%d", tm_local.tm_min);
                        sprintf(loc_sec, "%d", tm_local.tm_sec);
                        sprintf(loc_year, "%d", tm_local.tm_year);
                        sprintf(loc_mon, "%d", tm_local.tm_mon);
                        sprintf(loc_day, "%d", tm_local.tm_mday);

                        // 读取xml文件，更改其中的内容
                        xmlDocPtr doc_rsp_time; /* the resulting document tree */
                        xmlNode *root_element_rsp_time = NULL;
                        xmlNode *time_node = NULL;

                        doc_rsp_time = xmlReadFile(xml_GetSystemDateAndTime_respon, NULL, 0);
                        if (doc_rsp_time == NULL)
                        {
                            fprintf(stderr, "Failed to parse %s\n", xml_GetSystemDateAndTime_respon);
                        }

                        root_element_rsp_time = xmlDocGetRootElement(doc_rsp_time);

                        // UTCDateTime 下面的 Hour
                        time_node = find_node_by_name(find_node_by_name(root_element_rsp_time, "UTCDateTime"), "Hour");
                        // 更改
                        if (time_node != NULL)
                        {
                            printf("********get the UTCDateTime hour\n");
                            xmlNodeSetContent(time_node, BAD_CAST utc_hour);
                        }

                        // UTCDateTime 下面的 Minute
                        time_node = find_node_by_name(find_node_by_name(root_element_rsp_time, "UTCDateTime"), "Minute");
                        // 更改
                        if (time_node != NULL)
                        {
                            printf("********get the UTCDateTime Minute\n");
                            xmlNodeSetContent(time_node, BAD_CAST utc_min);
                        }

                        xmlChar *xmlbuff;
                        int buffersize;

                        xmlDocDumpFormatMemory(doc_rsp_time, &xmlbuff, &buffersize, 1);

                        onvif_respon(recv_buf, item, xmlbuff, buffersize);

                        xmlFreeDoc(doc_rsp_time);
                    }
                    else if (xmlStrcmp(body_child->name, BAD_CAST "GetStreamUri") == 0)
                    {
                        printf("****GetStreamUri is %s\n", xmlNodeGetContent(body_child));
                        xmlNode *node_proft = find_node_by_name(body_child, "ProfileToken");
                        if (node_proft == NULL)
                        {
                            return;
                        }
                        if (xmlStrcmp(xmlNodeGetContent(node_proft), BAD_CAST "MainStreamToken") == 0)
                        {
                            // 读取xml文件，更改其中的内容
                            xmlDocPtr doc_rsp_uri; /* the resulting document tree */
                            xmlNode *root_element_rsp_uri = NULL;

                            doc_rsp_uri = xmlReadFile(xml_GetStreamUri_respon, NULL, 0);
                            if (doc_rsp_uri == NULL)
                            {
                                fprintf(stderr, "Failed to parse %s\n", xml_GetStreamUri_respon);
                            }

                            root_element_rsp_uri = xmlDocGetRootElement(doc_rsp_uri);
                            xmlNode *node_uri = find_node_by_name(root_element_rsp_uri, "Uri");

                            char uri_tmp[100];
                            memset(uri_tmp, 0, sizeof(uri_tmp));
                            //rtsp://192.168.7.230:8554/live
                            //sprintf(uri_tmp, "rtsp://%s:%d/stream1", inet_ntoa(local_ip), rtsp_port);
                            sprintf(uri_tmp, "rtsp://%s:%d/live", inet_ntoa(local_ip), rtsp_port);

                            xmlNodeSetContent(node_uri, BAD_CAST uri_tmp);

                            xmlChar *xmlbuff;
                            int buffersize;

                            xmlDocDumpFormatMemory(doc_rsp_uri, &xmlbuff, &buffersize, 1);

                            onvif_respon(recv_buf, item, xmlbuff, buffersize);

                            xmlFreeDoc(doc_rsp_uri);
                        }
                        else
                        {
                            return;
                        }
                    }
                }
            }
        }
    }

    xmlFreeDoc(doc);
    // xmlCleanupParser();
}

int main(int argc, char *argv[])
{
    // if (2 != argc)
    //{
    //     printf("usage: %s port\n", argv[0]);
    //     return -1;
    // }
    // int port = atoi(argv[1]);
    memset(&sp_info, 0, sizeof(struct_sp));

    // 开启http服务，为接受onvif文件做准备
    // ret 所有的return值放这里
    int ret = -1;

    int http_socket_fd;
    struct in_addr http_server_addr;
    char ipbuf[100];
    int on = 1; // 端口重用

    http_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (http_socket_fd < 0)
    {
        perror("http_socket_fd");
        exit(1);
    }
    ret = setsockopt(http_socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    struct sockaddr_in http_sockaddr_in;
    int len;

    http_sockaddr_in.sin_family = AF_INET;        // 使用IPV4地址
    http_sockaddr_in.sin_port = htons(http_port); // 端口
    /* INADDR_ANY表示不管是哪个网卡接收到数据，只要目的端口是http_PORT，就会被该应用程序接收到 */
    http_sockaddr_in.sin_addr.s_addr = htonl(INADDR_ANY); // 自动获取IP地址
    len = sizeof(http_sockaddr_in);
    /* 绑定socket */
    if (bind(http_socket_fd, (struct sockaddr *)&http_sockaddr_in, len) < 0)
    {
        perror("http_port bind error:");
        exit(1);
    }

    if (listen(http_socket_fd, 2) < 0)
    {
        perror("http_port listen error:");
        exit(1);
    }

    // 发现程序
    ret = pthread_create(&probe_tid, NULL, (void *(*)(void *))probe_thread, NULL);

    fd_set active_rfds;
    FD_ZERO(&active_rfds); // 清零select的set合集

    int selected, i, max_fd;

    conn_list_t pool;
    TAILQ_INIT(&pool.head); // 初始化TAILQ队列
    pool.count = 0;         // TAILQ队列数量为0

    conn_item_t *item, *next;

    struct timeval tv;

    socklen_t http_size = sizeof(struct sockaddr_in);

    while (!daemon_exit)
    {
        fd_set rfds; //

        // 这里把活的active_rfds给rfds
        rfds = active_rfds;
        max_fd = -1;

        printf("%d connection **************\n", pool.count);
        if (pool.count < MAX_CONN_ACCEPT)
        {

            FD_SET(http_socket_fd, &rfds);
            max_fd = (http_socket_fd > max_fd) ? http_socket_fd : max_fd;

            TAILQ_FOREACH(item, &pool.head, entry)
            {
                max_fd = (item->fd > max_fd) ? item->fd : max_fd;
            }
        }
        else
        {
            printf("%d connection **************\n", pool.count);
        }

        tv.tv_sec = MAX_CONN_TIMEOUT;
        tv.tv_usec = 0;
        selected = select(max_fd + 1, &rfds, NULL, NULL, &tv);
        if (selected < 0)
        {
            printf("listen fd fail \n");
        }
        /* check and accept new connection */
        if (selected)
        {
            printf("selected \n");
            int is_accept = 0;
            if (http_socket_fd >= 0 && FD_ISSET(http_socket_fd, &rfds))
            { // listen_fd收到请求建立传输
                // 创建item，用来保存要建立的fd
                printf("connetion request \n");
                item = malloc(sizeof(*item));
                if (!item)
                    continue;

                // accept创建传输fd
                item->fd = accept(http_socket_fd, (struct sockaddr *)&http_sockaddr_in, &http_size);
                printf("accept from :%s :%d, len is: %d\n", inet_ntoa(http_sockaddr_in.sin_addr), ntohs(http_sockaddr_in.sin_port), http_size);

                if (item->fd >= 0)
                {
                    // 设置并把这个传输fd放到active_rfds
                    memcpy(&(item->usa.sa_in), &http_sockaddr_in, http_size);
                    setsockopt(item->fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
                    FD_SET(item->fd, &active_rfds);
                    TAILQ_INSERT_TAIL(&pool.head, item, entry);
                    pool.count++;
                    is_accept++;
                }
                else
                {
                    printf("accept fail\n");
                    free(item);
                }
            }
            else
            {
                printf("transt \n");
            }

            /* Continue waiting */
            // 如果是创建传输的fd，成功创建传输fd后，is_accept++，则继续等待。
            // 如果数据传输的fd，上面的条件不足，is_accept 0，跳过这一步，到处理
            if (is_accept)
                continue;
        }

        /* Check and process pending or expired requests */
        // 历遍item，看看是哪个传输fd发的请求
        TAILQ_FOREACH_SAFE(item, &pool.head, entry, next)
        {
            // 不是对应的fd继续
            if (selected && !FD_ISSET(item->fd, &rfds))
                continue;
            // 先将要处理的fd从active_rfds处理，下次再处理该ip发来的请求时再建传输fd？？？
            FD_CLR(item->fd, &active_rfds);
            TAILQ_REMOVE(&pool.head, item, entry);
            pool.count--;

            if (selected)
            {
                FILE *conn_fp;
                // 处理接受的信息
                conn_fp = fdopen(item->fd, "r+");
                if (conn_fp)
                {
                    // 主要处理函数
                    handle_request(conn_fp, item);
                    fflush(conn_fp);
                    shutdown(item->fd, SHUT_RDWR);
                    fclose(conn_fp);
                    conn_fp = NULL;
                    item->fd = -1;
                }
                if (--selected == 0)
                {
                    next = NULL;
                }
            }

            if (item->fd >= 0)
            {
                shutdown(item->fd, SHUT_RDWR);
                close(item->fd);
            }

            free(item);
        }
    }

    /* free all pending requests */
    TAILQ_FOREACH_SAFE(item, &pool.head, entry, next)
    {
        if (item->fd >= 0)
        {
            shutdown(item->fd, SHUT_RDWR);
            close(item->fd);
        }

        free(item);
    }

    if (http_socket_fd >= 0)
    {
        shutdown(http_socket_fd, SHUT_RDWR);
        close(http_socket_fd);
    }

    printf("main thread exit!\n");

    return 0;
}